import Utils (splitAll, findSubList, unwarp)
import Data.Char (isDigit)
import Data.List (zipWith)

type BagName = String

type SubBagInfo = (String, Int)
type SubBagInfos = [SubBagInfo]

type Bag = (BagName,SubBagInfos)
type Bags = [Bag]

parseSubBag :: String -> SubBagInfo
parseSubBag string = (name, number)
    where
        numberString = takeWhile isDigit string
        number = read numberString

        string' = tail . dropWhile isDigit $ string

        bagIndex = unwarp . findSubList "bag" $ string'
        name = take (bagIndex - 1) string'

parseLine :: String -> Bag
parseLine string = 
    if null firstSubBagToken
        then (name, [])
        else (name, map parseSubBag subBagTokens)
    where
        tokens = splitAll "," string
        firstToken = head tokens
        
        bagIndex = unwarp . findSubList "bag" $ firstToken
        name = take (bagIndex - 1) firstToken

        firstSubBagToken = dropWhile (not . isDigit) firstToken
        subBagTokens = firstSubBagToken : (map tail . tail $ tokens)

parseInput :: String -> Bags
parseInput = map parseLine . lines  

containsCount :: Bags -> BagName -> Int
containsCount bags name = 
    1 + sum (zipWith (*) subBagCounts subBagSize)
    where
        subBagInfos = unwarp . lookup name $ bags
        subBagNames = map fst subBagInfos
        subBagSize = map snd subBagInfos
        subBagCounts = map (containsCount bags) subBagNames

main :: IO ()
main = do
    input <- readFile "./input.txt" 
    let bags = parseInput input
    print . (\x -> x-1) . containsCount bags $ "shiny gold"